package cash.z.ecc.android.integration

import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import cash.z.ecc.android.lockbox.LockBox
import cash.z.ecc.kotlin.mnemonic.Mnemonics
import cash.z.ecc.android.sdk.Initializer
import okio.Buffer
import okio.GzipSink
import okio.Okio
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class IntegrationTest {

    private lateinit var appContext: Context
    private val mnemonics = Mnemonics()
    private val phrase =
        "human pulse approve subway climb stairs mind gentle raccoon warfare fog roast sponsor" +
                " under absorb spirit hurdle animal original honey owner upper empower describe"

    @Before
    fun start() {
        appContext = InstrumentationRegistry.getInstrumentation().targetContext
    }

    @Test
    fun testSeed_generation() {
        val seed = mnemonics.toSeed(phrase.toCharArray())
        assertEquals(
            "Generated incorrect BIP-39 seed!",
            "f4e3d38d9c244da7d0407e19a93c80429614ee82dcf62c141235751c9f1228905d12a1f275f" +
                    "5c22f6fb7fcd9e0a97f1676e0eec53fdeeeafe8ce8aa39639b9fe",
            seed.toHex()
        )
    }

    @Test
    fun testSeed_storage() {
        val seed = mnemonics.toSeed(phrase.toCharArray())
        val lb = LockBox(appContext)
        lb.setBytes("seed", seed)
        assertTrue(seed.contentEquals(lb.getBytes("seed")!!))
    }

    @Test
    fun testPhrase_storage() {
        val lb = LockBox(appContext)
        val phraseChars = phrase.toCharArray()
        lb.setCharsUtf8("phrase", phraseChars)
        assertTrue(phraseChars.contentEquals(lb.getCharsUtf8("phrase")!!))
    }

    @Test
    fun testPhrase_maxLengthStorage() {
        val lb = LockBox(appContext)
        // find and expose the max length
        var acceptedSize = 256
        while (acceptedSize > 0) {
            try {
                lb.setCharsUtf8("temp", nextString(acceptedSize).toCharArray())
                break
            } catch (t: Throwable) {
            }
            acceptedSize--
        }

        val maxSeedPhraseLength = 8 * 24 + 23  //215 (max length of each word is 8)
        assertTrue(
            "LockBox does not support the maximum length seed phrase." +
                    " Expected: $maxSeedPhraseLength but was: $acceptedSize",
            acceptedSize > maxSeedPhraseLength
        )
    }

    @Test
    fun testAddress() {
        val seed = mnemonics.toSeed(phrase.toCharArray())
        val initializer = Initializer(appContext) { config ->
            config.new(seed)
        }
        assertEquals(
            "Generated incorrect z-address!",
            "zs1gn2ah0zqhsxnrqwuvwmgxpl5h3ha033qexhsz8tems53fw877f4gug353eefd6z8z3n4zxty65c",
            initializer.rustBackend.getAddress()
        )
        initializer.clear()
    }


    private fun ByteArray.toHex(): String {
        val sb = StringBuilder(size * 2)
        for (b in this)
            sb.append(String.format("%02x", b))
        return sb.toString()
    }

    fun String.gzip(): ByteArray {
        val result = Buffer()
        val sink = Okio.buffer(GzipSink(result))
        sink.use {
            sink.write(toByteArray())
        }
        return result.readByteArray()
    }

    fun nextString(length: Int): String {
        val allowedChars = "ACGT"
        return (1..length)
            .map { allowedChars.random() }
            .joinToString("")
    }
}